const Redis = require("ioredis");

/**
 * RedisService 类封装了与Redis数据库交互的基本操作。
 */
class RedisService {
  /**
   * 构造函数初始化Redis客户端连接。
   */
  constructor() {
    this.client = new Redis({
      host: process.env.REDIS_HOST, // Redis服务器地址
      port: process.env.REDIS_PORT, // Redis服务端口
      password: process.env.REDIS_PASSWORD, // Redis密码（如有）
      db: process.env.REDIS_DB, // 选择的数据库索引
    });
  }

  /**
   * 获取指定键的值。
   * @param {string} key - 键名。
   * @returns {Promise<string|Buffer|null>} 返回键对应的值，如果键不存在则返回null。
   */
  async get(key) {
    return this.client.get(key);
  }

  /**
   * 设置键值对。可选设置过期时间。
   * @param {string} key - 键名。
   * @param {number|Buffer} value - 要存储的值。
   * @param {null} [expires] - 键的过期时间（秒）。默认为null，表示永不过期。
   * @returns {Promise<string|boolean>} 成功返回'OK'或true，失败则抛出错误。
   */
  async set(key, value, expires = null) {
    if (expires) {
      return this.client.setex(key, expires, value);
    } else {
      return this.client.set(key, value);
    }
  }

  /**
   * 删除一个或多个键。
   * @param {string|string[]} key - 单个键名或键名数组。
   * @returns {Promise<number>} 成功删除的键的数量。
   */
  async delete(key) {
    return this.client.del(key);
  }

  /**
   * 检查键是否存在。
   * @param {string} key - 键名。
   * @returns {Promise<number>} 存在返回1，不存在返回0。
   */
  async exists(key) {
    return this.client.exists(key);
  }

  /**
   * 为键自增1。
   * @param {string} key - 键名。
   * @returns {Promise<number>} 自增后的值。
   */
  async incr(key) {
    return this.client.incr(key);
  }

  /**
   * 为键自减1。
   * @param {string} key - 键名。
   * @returns {Promise<number>} 自减后的值。
   */
  async decr(key) {
    return this.client.decr(key);
  }

  /**
   * 设置键的过期时间。
   * @param {string} key - 键名。
   * @param {number} seconds - 键的过期时间（秒）。
   * @returns {Promise<number>} 设置成功返回1，否则返回0。
   */
  async expire(key, seconds) {
    return this.client.expire(key, seconds);
  }

  /**
   * 获取键的剩余生存时间（TTL）。
   * @param {string} key - 键名。
   * @returns {Promise<number>} 键的剩余生存时间（秒），如果键不存在或没有设置过期时间则返回-1或-2。
   */
  async ttl(key) {
    return this.client.ttl(key);
  }

  // 哈希表操作方法...

  /**
   * 在哈希表中设置字段值。
   * @param {string} hashKey - 哈希表的键名。
   * @param {string} field - 字段名。
   * @param {string|Buffer} value - 字段值。
   * @returns {Promise<number>} 如果字段是新创建的返回1，如果字段已存在且值被更新返回0。
   */
  async hset(hashKey, field, value) {
    return this.client.hset(hashKey, field, value);
  }

  /**
   * 获取哈希表中指定字段的值。
   * @param {string} hashKey - 哈希表的键名。
   * @param {string} field - 字段名。
   * @returns {Promise<string|Buffer|null>} 字段的值，如果字段不存在则返回null。
   */
  async hget(hashKey, field) {
    return this.client.hget(hashKey, field);
  }

  /**
   * 获取哈希表中所有字段和值。
   * @param {string} hashKey - 哈希表的键名。
   * @returns {Promise<Object<string,string|Buffer>>} 包含所有字段和值的对象。
   */
  async hgetall(hashKey) {
    return this.client.hgetall(hashKey);
  }

  /**
   * 删除哈希表中的一个或多个字段。
   * @param {string} hashKey - 哈希表的键名。
   * @param {string[]} fields - 要删除的字段数组。
   * @returns {Promise<number>} 成功删除的字段数量。
   */
  async hdel(hashKey, ...fields) {
    return this.client.hdel(hashKey, ...fields);
  }

  // 列表操作
  async lpush(key, ...values) {
    /**
     * 在列表左侧插入一个或多个值。
     * @param {string} key - 列表键名。
     * @param {...string|Buffer} values - 要插入的值。
     * @returns {Promise<number>} 执行LPUSH命令后，列表的长度。
     */
    return this.client.lpush(key, ...values);
  }

  async rpop(key) {
    /**
     * 移除并返回列表的最后一个元素。
     * @param {string} key - 列表键名。
     * @returns {Promise<string|Buffer|null>} 列表的最后一个元素，如果列表为空则返回null。
     */
    return this.client.rpop(key);
  }

  // 集合操作
  async sadd(key, member) {
    /**
     * 将一个成员添加到集合中。
     * @param {string} key - 集合键名。
     * @param {string|Buffer} member - 要添加的成员。
     * @returns {Promise<number>} 如果成员是新添加的返回1，如果成员已经存在则返回0。
     */
    return this.client.sadd(key, member);
  }

  async smembers(key) {
    /**
     * 返回集合中的所有成员。
     * @param {string} key - 集合键名。
     * @returns {Promise<Array<string|Buffer>>} 集合中的所有成员。
     */
    return this.client.smembers(key);
  }

  // 有序集合操作
  async zadd(key, score, member) {
    /**
     * 添加一个成员到有序集合，或者更新已存在成员的分数。
     * @param {string} key - 有序集合键名。
     * @param {number} score - 成员的分数。
     * @param {string|Buffer} member - 成员。
     * @returns {Promise<number>} 被添加的新成员的数量，不包括分数被更新的已存在成员。
     */
    return this.client.zadd(key, score, member);
  }

  async zrange(key, start, stop, withScores = false) {
    /**
     * 返回有序集合中指定范围的成员，成员的位置按分数值递增(从小到大)来排序。
     * @param {string} key - 有序集合键名。
     * @param {number} start - 开始索引。
     * @param {number} stop - 结束索引。
     * @param {boolean} [withScores=false] - 是否同时返回成员的分数。
     * @returns {Promise<Array<string|Buffer>|Array<[string|Buffer,number]>>} 成员列表，如果withScores为true，则每个成员后面跟着它的分数。
     */
    return this.client.zrange(key, start, stop, "WITHSCORES");
  }

  // 事务操作
  async multi() {
    /**
     * 开启一个事务，返回事务上下文。
     * @returns {Promise<RedisMulti>} 事务上下文，可以用来链式调用事务内的命令。
     */
    return this.client.multi();
  }

  // 发布订阅
  async publish(channel, message) {
    /**
     * 将消息发布到指定的频道。
     * @param {string} channel - 频道名。
     * @param {string|Buffer} message - 要发布的消息。
     * @returns {Promise<number>} 收到消息的订阅者数量。
     */
    return this.client.publish(channel, message);
  }

  async subscribe(channel, listener) {
    /**
     * 订阅给定的一个或多个频道的信息。
     * @param {string|string[]} channel - 频道名或频道名数组。
     * @param {Function} listener - 消息处理函数，接收channel和message作为参数。
     */
    this.client.on("message", listener);
    return this.client.subscribe(channel);
  }

  // ...根据需要继续添加其他方法...
}

module.exports = new RedisService();
